/***************************************************************************
 *   Copyright (C) 2007 by Warren Kenny   *
 *   warren.kenny@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include <otaSession.h>

otaSession::otaSession()
: m_idSet( false ),
  m_state(OTA_SESSION_UNINITIALIZED),
  m_sequenceID( 0 ),
  m_lastSequence( 0 ),
  m_username( "Unknown" ),
  m_database( "Unknown" )
{
}

void otaSession::registerListener( otaSessionListener* listener )
{
	m_listeners.push_back( listener );
}
	
void otaSession::setInitData( const otaSessionKey key, unsigned long serverIP, unsigned short serverPort, unsigned long clientIP, unsigned short clientPort )
{
	m_myKey = key;
	m_serverIP = serverIP;
	m_clientIP = clientIP;
	m_serverPort = serverPort;
	m_clientPort = clientPort;
}
	
void otaSession::addPacket( const otaSessionKey& key, const otaTime arrivalTime, const char *data, const unsigned int dataSize, const unsigned int offset, unsigned int &newOffset )
{
	std::cout << "Session State = " << m_state << std::endl; 
	bool fromServer = sourceServer( key );
	m_sessionLastPacket = arrivalTime;
	switch( m_state )
	{
		case OTA_SESSION_UNINITIALIZED:
		{
			m_sessionStart = arrivalTime;
			if( fromServer )
			{
				otaServerHandshake handshake;
				if( handshake.deserialize( data, dataSize, offset, newOffset ) )
				{
					std::cout << "Successfully deserialized handshake" << std::endl;
					m_serverThreadID = handshake.getThreadID();
					m_state = OTA_SESSION_CLIENT_CREDENTIALS_PENDING;
				}
			}
		}
		break;
		
		case OTA_SESSION_CLIENT_CREDENTIALS_PENDING:
		{
			if( fromServer )
			{
				m_state = OTA_SESSION_INVALID;
			}
			else
			{
				otaClientCredentials credentials;
				if(credentials.deserialize( data, dataSize, offset, newOffset ) )
				{
					m_username = credentials.getUsername();
					m_database = credentials.getDatabase();
					m_clientFlags = credentials.getClientFlags();
					m_state = OTA_SESSION_CLIENT_LOGON_CONFIRMATION_PENDING;
				}
			}
		}
		break;
		
		case OTA_SESSION_CLIENT_CREDENTIALS_OLD_FORMAT_PENDING:
		{
			if( fromServer )
			{
				m_state = OTA_SESSION_INVALID;
			}
			else
			{
				m_state = OTA_SESSION_CLIENT_LOGON_CONFIRMATION_PENDING;
			}
		}
		break;
		
		case OTA_SESSION_CLIENT_LOGON_CONFIRMATION_PENDING:
		{
			if( fromServer )
			{
				otaServerResponse response;
				if(response.deserialize( data, dataSize, offset, newOffset ))
				{
					if( response.getType() == OTA_SERVER_RESPONSE_OK )
					{
						m_state = OTA_SESSION_ACCEPTING_COMMANDS;
					}
					else if( response.getType() == OTA_SERVER_RESPONSE_REFORMAT_PASSWORD )
					{
						m_state = OTA_SESSION_CLIENT_CREDENTIALS_OLD_FORMAT_PENDING;
					}
					else
					{
						m_state = OTA_SESSION_CLIENT_LOGON_FAILURE;
						std::vector< otaSessionListener* >::iterator it;
						for( it = m_listeners.begin() ; it != m_listeners.end(); it++ )
						{
							otaSessionListener *listener = *it;
							listener->onSessionLogonFail( m_myKey );
						}
					}
				}
				
			}
			else
			{
				m_state = OTA_SESSION_INVALID;
			}	
		}
		break;
		
		case OTA_SESSION_INIT_DB_CONFIRMATION_PENDING:
		{
			if( fromServer )
			{
				otaServerResponse response;
				if(response.deserialize( data, dataSize, offset, newOffset ))
				{
					if( response.getType() == OTA_SERVER_RESPONSE_OK )
					{
						m_database = m_tempDatabase;
					}
					m_state = OTA_SESSION_ACCEPTING_COMMANDS;
				}
			}
			else
			{
				m_state = OTA_SESSION_ACCEPTING_COMMANDS;
			}
		}
		break;
		
		case OTA_SESSION_ACCEPTING_COMMANDS:
		{
			if( !fromServer )
			{
				otaClientCommand command = m_clientCommands[ m_sequenceID ];
				command.setDatabase( m_database );
				if( command.deserialize( arrivalTime, data, dataSize, offset, newOffset ) )
				{
					if(command.getType() != OTA_CLIENT_COMMAND_IRRELEVANT )	
					{
						m_sequenceID++;
						m_state = OTA_SESSION_READY;
					}
				}
			}
		}
		break;
		
		case OTA_SESSION_READY:
		{
			if( fromServer )
			{
				otaServerResponse response;
				if(response.deserialize( data, dataSize, offset, newOffset ) )
				{
					m_clientCommands[m_sequenceID].processResponse( arrivalTime, response );
				}
			}
			else
			{
				otaClientCommand command = m_clientCommands[ m_sequenceID ];
				command.setDatabase( m_database );
				if( command.deserialize( arrivalTime, data, dataSize, offset, newOffset ) )
				{
					switch(command.getType())
					{
						case OTA_CLIENT_COMMAND_IRRELEVANT:
						break;
						
						case OTA_CLIENT_COMMAND_INIT_DB:
							m_sequenceID++;
							m_state = OTA_SESSION_INIT_DB_CONFIRMATION_PENDING;
						break;
						
						default:
							m_sequenceID++;
							m_state = OTA_SESSION_READY;
					}
				}
			}
		}
		
		case OTA_SESSION_INVALID:
		{
			if( !fromServer )
			{
				otaClientCommand command;
				if( command.deserialize( arrivalTime, data, dataSize, offset, newOffset ) )
				{
					if( command.getType() == OTA_CLIENT_COMMAND_QUIT )
					{
						std::vector< otaSessionListener* >::iterator it;
						for( it = m_listeners.begin() ; it != m_listeners.end(); it++ )
						{
							otaSessionListener *listener = *it;
							listener->onSessionEnd( m_myKey );
						}
					}
				}
			}
		}
	}
}

bool otaSession::sourceServer( const otaSessionKey& key )
{
	return key.getSourceIP() == m_serverIP;
}

bool otaSession::getSessionID( unsigned long &sessionID )
{
	if( m_idSet )
		sessionID = m_sessionID;	
	return m_idSet;
}

void otaSession::setSessionID( unsigned long sessionID )
{
	m_idSet = true;
	m_sessionID = sessionID;
}

unsigned long otaSession::getServerIP()
{
	return m_serverIP;
}
	
unsigned long otaSession::getClientIP()
{
	return m_clientIP;
}
	
unsigned short otaSession::getServerPort()
{
	return m_serverPort;
}
	
unsigned short otaSession::getClientPort()
{
	return m_clientPort;
}
	
unsigned int otaSession::getThreadID()
{
	return m_serverThreadID;
}	

std::string otaSession::getUsername()
{
	return m_username;
}
	
otaTime otaSession::getStartTime()
{
	return m_sessionStart;
}
	
otaTime otaSession::getEndTime()
{
	return m_sessionLastPacket;
}
	
bool otaSession::getCommand( unsigned long &sequenceID, otaClientCommand** command )
{
	std::map<unsigned long, otaClientCommand>::iterator it = m_clientCommands.find( m_lastSequence );
	if( it != m_clientCommands.end() )
	{
		sequenceID = m_lastSequence;
		*command = &(it->second);
		m_lastSequence++;
		return true;
	}
	return false;
}

bool otaSession::isValid()
{
	return (m_state > OTA_SESSION_CLIENT_LOGON_CONFIRMATION_PENDING);
}

otaSession::~otaSession()
{
}


